/*cineplex.c*/

#include "cineplex.h"

void startCineplex()
{
	NUM_CUSTOMERS = MAX_CUSTOMERS;
	NUM_TIX_CLERKS = MAX_TIX_CLERKS;
	NUM_FOOD_CLERKS = MAX_FOOD_CLERKS;
	NUM_TIX_TAKERS = MAX_TIX_TAKERS;
	NUM_ROWS = MAX_ROWS;
	NUM_COLS = MAX_COLS;
	
	for(i = 0; i < MAX_ROWS * MAX_COLS; i++)
	{
		seating[i] = -1;
	}
	for(i = 0; i < MAX_CUSTOMERS; i++)
	{
		isLeader[i] = 0;
	}
	
	/*init stuff*/
	custLock = CreateLock("custLock", 8);
	initLock = CreateLock("initLock",8);
	loopLock = CreateLock("loopLock",8);
	tempLock = CreateLock("tempLock",8);
	poolLock = CreateLock("poolLock",8);
	agentsStarted = 0;
	
	outputLen = 0;
	
	nextC = 0;
	nextTC = 0;
	nextFC = 0;
	nextTT = 0;
	
	letPeopleIn = 0;
	runSchedulers = 1;
	
	/*theater data*/
	seatingLock = CreateLock("seatingLock", 11);
	outputLen = Itoa(seatingLock, outBuff);
	Write(outBuff,outputLen,ConsoleOutput);
	Write("\n",1,ConsoleOutput);
	seatsFilled = 0;
	totalSeats = NUM_ROWS * NUM_COLS;
	movieStarted = 0;

	/*bathroom*/
	brLock  = CreateLock("brLock", 6);

	/*locks*/
	tixLinesLock = CreateLock("tixLinesLock", 12);
	foodLinesLock = CreateLock("foodLinesLock", 13);
	entryLinesLock = CreateLock("entryLinesLock", 14);
	initCond = CreateCondition("initCond", 8);
	
	/*assorted variables*/
	tixPassedOut = 0;
	popcornSold = 0;
	sodaSold = 0;
	customersRunning = NUM_CUSTOMERS;
	
	/*GENERATE CUSTOMERS
	(and the ticket pool while we're at it)*/
	
	for(i = 0; i < MAX_CUSTOMERS/MAX_GROUP_SIZE + 1; i++)
	{
		groupSizes[i] = 0;
		for(k = 0; k < MAX_GROUP_SIZE; k++)
		{
			groups[i][k] = -1;
		}
	}
	
	j = 0; /*j will be numGroups*/
	for(i = 0; i < NUM_CUSTOMERS; i++)
	{
		ticketPool[i].num = i;
		sodaPool[i].num = i;
		popcornPool[i].num = i;
		/*sprintf(name, "C-%d", i);*/
		
		if(i == NUM_CUSTOMERS-1) /*f last Customer*/
		{
			/*if(!testPattern) printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());*/
			ConsoleAcquire();
			Write("Customer ", 8, ConsoleOutput);
			outputLen = Itoa(i, outBuff);
			Write(outBuff , outputLen, ConsoleOutput);
			Write(" is a leader with ", 18, ConsoleOutput);
			outputLen = Itoa(groupSizes[j], outBuff);
			Write(outBuff , outputLen, ConsoleOutput);
			Write(" subs!\n", 7, ConsoleOutput);
			ConsoleRelease();
			groups[j][groupSizes[j]] = i;
			groupSizes[j] = groupSizes[j] + 1;
			groupNums[i] = j;
			isLeader[i] = 1;
			j = j + 1;
		}else if(groupSizes[j] < 2)
		{
			/*printf("Customer %s of group %d is waiting for the HeadCustomer.\n",name,j+1);*/
			ConsoleAcquire();
			Write("Customer ", 8, ConsoleOutput);
			outputLen = Itoa(i, outBuff);
			Write(outBuff , outputLen, ConsoleOutput);
			Write(" of group ", 10, ConsoleOutput);
			outputLen = Itoa(j, outBuff);
			Write(outBuff , outputLen, ConsoleOutput);
			Write(" is waiting for the head customer\n", 34, ConsoleOutput);
			ConsoleRelease();
			groups[j][groupSizes[j]] = i;
			groupSizes[j] = groupSizes[j] + 1;
			groupNums[i] = j;
		}else if(groupSizes[j] < MAX_GROUP_SIZE-1)
		{
			if(getRandPercent() < 33)
			{
				/*printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());*/
				ConsoleAcquire();
				Write("Customer ", 8, ConsoleOutput);
				outputLen = Itoa(i, outBuff);
				Write(outBuff , outputLen, ConsoleOutput);
				Write(" is a leader with ", 18, ConsoleOutput);
				outputLen = Itoa(groupSizes[j], outBuff);
				Write(outBuff , outputLen, ConsoleOutput);
				Write(" subs!\n", 7, ConsoleOutput);
				ConsoleRelease();
				groups[j][groupSizes[j]] = i;
				groupSizes[j] = groupSizes[j] + 1;
				groupNums[i] = j;
				isLeader[i] = 1;
				j = j + 1;
			}else{
				/*printf("Customer %s of group %d is waiting for the HeadCustomer.\n",name,j+1);*/
				ConsoleAcquire();
				Write("Customer ", 8, ConsoleOutput);
				outputLen = Itoa(i, outBuff);
				Write(outBuff , outputLen, ConsoleOutput);
				Write(" of group ", 10, ConsoleOutput);
				outputLen = Itoa(j, outBuff);
				Write(outBuff , outputLen, ConsoleOutput);
				Write(" is waiting for the head customer\n", 34, ConsoleOutput);
				ConsoleRelease();
				groups[j][groupSizes[j]] = i;
				groupSizes[j] = groupSizes[j] + 1;
				groupNums[i] = j;
			}
		}else{
			/*printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());*/
			ConsoleAcquire();
			Write("Customer ", 8, ConsoleOutput);
			outputLen = Itoa(i, outBuff);
			Write(outBuff , outputLen, ConsoleOutput);
			Write(" is a leader with ", 18, ConsoleOutput);
			outputLen = Itoa(groupSizes[j], outBuff);
			Write(outBuff , outputLen, ConsoleOutput);
			Write(" subs!\n", 7, ConsoleOutput);
			ConsoleRelease();
			groups[j][groupSizes[j]] = i;
			groupSizes[j] = groupSizes[j] + 1;
			groupNums[i] = j;
			isLeader[i] = 1;
			j = j + 1;
		}
	}
	numGroups = j;
	
	Acquire(loopLock);
	/*for(i = 0; i < MAX_CUSTOMERS; i++)
	{
		isLeader[i] = 0;
	}

	for(i = 0; i < numGroups; i++)
	{
		isLeader[groups[i][groupSizes[groupNums[i]]-1]] = 1;
		ConsoleAcquire();
		Write("Is leader ", 10, ConsoleOutput);
		outputLen = Itoa(groups[i][groupSizes[groupNums[i]]-1],outBuff);
		Write(outBuff, outputLen, ConsoleOutput);
		Write("\n", 1, ConsoleOutput);
		ConsoleRelease();
	}*/
	
	Acquire(initLock);
	for(i = 0; i < NUM_CUSTOMERS; i++)
	{
		Fork((void*)startCustomer);
	}
	Release(loopLock);

	Acquire(loopLock);
	/*GENERATE CLERKS*/
	for(i = 0; i < NUM_TIX_CLERKS; i++)
	{
		Write( "TC\n", 3, ConsoleOutput );
		Fork(startTC);
	}
	Release(loopLock);
	
	Acquire(loopLock);
	for(i = 0; i < NUM_FOOD_CLERKS; i++)
	{
		Write( "FC\n", 3, ConsoleOutput );
		Fork(startFC);
	}
	Release(loopLock);
	
	Acquire(loopLock);
	for(i = 0; i < NUM_TIX_TAKERS; i++)
	{
		Write( "TT\n", 3, ConsoleOutput );
		Fork(startTT);
	}
	Release(loopLock);
	
	Fork(startManager);
	Fork(startTech);
	Fork(startLobby);
	
	Release(initLock);
	
	while(agentsStarted < NUM_TIX_CLERKS +  NUM_FOOD_CLERKS +  NUM_TIX_TAKERS +  NUM_CUSTOMERS + 2)
	{
		Yield();
	}
	Acquire(initLock);
	Broadcast(initCond, initLock); /*allow threads to begin*/
	Write( "Broadcast sent\n", 15, ConsoleOutput );
	Release(initLock);
	
	/*INITIAL OUTPUT*/
	/*printf("NUMBER OF CUSTOMERS: %d\n", NUM_CUSTOMERS);*/
	ConsoleAcquire();
	Write("NUMBER OF CUSTOMERS: ", 21, ConsoleOutput);
	outputLen = Itoa(NUM_CUSTOMERS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*printf("NUMBER OF GROUPS: %d\n", j);*/
	/*printf("NUMBER OF TICKET CLERKS: %d\n", NUM_TIX_CLERKS);*/
	ConsoleAcquire();
	Write("NUMBER OF TICKET CLERKS: ", 25, ConsoleOutput);
	outputLen = Itoa(NUM_TIX_CLERKS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*printf("NUMBER OF FOOD CLERKS: %d\n", NUM_FOOD_CLERKS);*/
	ConsoleAcquire();
	Write("NUMBER OF FOOD CLERKS: ", 23, ConsoleOutput);
	outputLen = Itoa(NUM_FOOD_CLERKS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*printf("NUMBER OF TICKET TAKERS: %d\n", NUM_TIX_TAKERS);*/
	ConsoleAcquire();
	Write("NUMBER OF TICKET TAKERS: ", 25, ConsoleOutput);
	outputLen = Itoa(NUM_TIX_CLERKS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*printf("NUMBER OF SEATS: %d\n", NUM_ROWS*NUM_COLS);*/
	ConsoleAcquire();
	Write("NUMBER OF SEATS: ", 17, ConsoleOutput);
	outputLen = Itoa(NUM_ROWS*NUM_COLS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*printf("NUMBER OF ROWS: %d\n", NUM_ROWS);*/
	ConsoleAcquire();
	Write("NUMBER OF ROWS: ", 16, ConsoleOutput);
	outputLen = Itoa(NUM_ROWS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*printf("NUMBER OF COLS: %d\n", NUM_COLS);*/
	ConsoleAcquire();
	Write("NUMBER OF COLS: ", 16, ConsoleOutput);
	outputLen = Itoa(NUM_COLS, outBuff);
	Write(outBuff , outputLen, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
	ConsoleRelease();
	/*ConsoleAcquire();
	Write("exit0\n", 6, ConsoleOutput);
	ConsoleRelease();*/
	Exit(0);
}

static void startCustomer()
{
	ConsoleAcquire();
	Acquire(initLock);
	customers[nextC].index = i;
	customers[nextC].replyTo = -1;
	customers[nextC].msgLock = CreateLock("cMsgLock",8); /*use for data access as well*/
	customers[nextC].sleepCon = CreateCondition("cSleep",6);
	customers[nextC].myName = nextC;
	customers[nextC].repliesWaiting = 0;
	customers[nextC].state = JOINTIXLINE;
	customers[nextC].numSubs = groupSizes[groupNums[nextC]]-1;
	customers[nextC].numPopcorn = 0;
	customers[nextC].numSoda = 0;
	customers[nextC].myRow = -1;
	customers[nextC].myCol = -1;
	customers[nextC].groupNumber = groupNums[nextC];
	customers[nextC].subsInBR = 0;
	customers[nextC].tickets = -1;
	customers[nextC].popcorns = -1;
	customers[nextC].sodas = -1;
	customers[nextC].money = 100000;
	customers[nextC].currentLine = -1; /*should know what type from state*/
	customers[nextC].numMsgs = 0;

	Acquire(loopLock);
	for(i = 0; i < MAX_GROUP_SIZE + 1; i++)
	{
		customers[nextC].msgList[i].name = NullMsg;
		customers[nextC].msgList[i].sender = NullMsg;
	}	
	Release(loopLock);

	if(!isLeader[nextC])
	{
		customers[nextC].money = 0;
		customers[nextC].state = SNEWSUB;
		customers[nextC].numSubs = 0;
	}
	ConsoleRelease();
	
	nextC = nextC + 1;
	Customer_Scheduler(nextC-1);
}

static void startTC()
{
	Acquire(initLock);
	tixClerks[nextTC].index = nextTC;
	tixClerks[nextTC].replyTo = -1;
	tixClerks[nextTC].msgLock = CreateLock("tcMsgLock", 9);
	tixClerks[nextTC].myName = nextTC;
	tixClerks[nextTC].sleepCon = CreateCondition("tcSleepCon", 10);
	tixClerks[nextTC].lineLength = 0;
	tixClerks[nextTC].moneyCollected = 0;
	tixClerks[nextTC].onBreak = 0;
	tixClerks[nextTC].status = IDLE;
	tixClerks[nextTC].TICKET_PRICE = 12;
	tixClerks[nextTC].numMsgs = 0;
	Acquire(loopLock);
	for(i = 0; i < 3; i++)
	{
		tixClerks[nextTC].msgList[i].name = NullMsg;
		tixClerks[nextTC].msgList[i].sender = NullMsg;
	}
	Release(loopLock);
	tixLines[nextTC].isOpen =1;
	tixLines[nextTC].myType = T_TICKETCLERK;
	tixLines[nextTC].index = nextTC;
	tixLines[nextTC].length = 0;
	Acquire(loopLock);
	for(i = 0; i < MAX_CUSTOMERS/MAX_GROUP_SIZE + 1; i++)
	{
		tixLines[nextTC].customers[i] = -1;
	}
	Release(loopLock);	
	nextTC = nextTC + 1;
	TicketClerk_Scheduler(nextTC-1);
}

static void startFC()
{
	Acquire(initLock);
	foodClerks[nextFC].index = nextFC;
	foodClerks[nextFC].replyTo = -1;
	foodClerks[nextFC].msgLock = CreateLock("fcMsgLock", 9);
	foodClerks[nextFC].myName = nextFC;
	foodClerks[nextFC].sleepCon = CreateCondition("fcSleepCon", 10);
	foodClerks[nextFC].moneyCollected = 0;
	foodClerks[nextFC].onBreak = 0;
	foodClerks[nextFC].status = IDLE;
	foodClerks[nextFC].POPCORN_PRICE = 5;
	foodClerks[nextFC].SODA_PRICE = 4;
	foodClerks[nextFC].numMsgs = 0;
	Acquire(loopLock);
	for(i = 0; i < 3; i++)
	{
		foodClerks[nextFC].msgList[i].name = NullMsg;
		foodClerks[nextFC].msgList[i].sender = NullMsg;
	}
	Release(loopLock);
	foodLines[nextFC].isOpen =1;
	foodLines[nextFC].myType = T_FOODCLERK;
	foodLines[nextFC].index = nextFC;
	foodLines[nextFC].length = 0;
	Acquire(loopLock);
	for(i = 0; i < MAX_CUSTOMERS/MAX_GROUP_SIZE + 1; i++)
	{
		foodLines[nextFC].customers[i] = -1;
	}
	Release(loopLock);	
	nextFC = nextFC + 1;
	FoodClerk_Scheduler(nextFC-1);
}

static void startTT()
{
Acquire(initLock);
	tixTakers[nextTT].index = nextTT;
	tixTakers[nextTT].replyTo = -1;
	tixTakers[nextTT].msgLock = CreateLock("tcMsgLock", 9);
	tixTakers[nextTT].myName = nextTT;
	tixTakers[nextTT].sleepCon = CreateCondition("tcSleepCon", 10);
	tixTakers[nextTT].onBreak = 0;
	tixTakers[nextTT].status = IDLE;
	tixTakers[nextTT].SizeOfGroupNotInTheater = 0;
	tixTakers[nextTT].numMsgs = 0;
	Acquire(loopLock);
	for(i = 0; i < 3; i++)
	{
		tixTakers[nextTT].msgList[i].name = NullMsg;
		tixTakers[nextTT].msgList[i].sender = NullMsg;
	}
	Release(loopLock);
	entryLines[nextTT].isOpen =1;
	entryLines[nextTT].myType = T_TICKETTAKER;
	entryLines[nextTT].index = nextTT;
	entryLines[nextTT].length = 0;
	Acquire(loopLock);
	for(i = 0; i < MAX_CUSTOMERS/MAX_GROUP_SIZE + 1; i++)
	{
		entryLines[nextTT].customers[i] = -1;
	}
	Release(loopLock);	
	nextTT = nextTT + 1;
	TicketTaker_Scheduler(nextTT-1);
}

static void startManager()
{
	Acquire(initLock);
	manager.index = 0;
	manager.replyTo = -1;
	manager.msgLock = CreateLock("mMsgLock",8);
	manager.myName = 0;
	manager.sleepCon = CreateCondition("mSleepCon",9);
	manager.state = IDLE;
	
	manager.checkQueueSize = 0;
	manager.cgSize = 0;
	manager.msgsToSend = 0;
	manager.repliesWaiting = 0;
	manager.moviePlaying = 0;
	manager.checkLoop = 0;
	manager.cashLoop = 0;
	manager.tixCash = 0;
	manager.foodCash = 0;
	manager.numMsgs = 0;
	for(i = 0; i < MAX_TIX_CLERKS + MAX_FOOD_CLERKS; i++)
	{
		manager.msgList[i].name = NullMsg;
		manager.msgList[i].sender = NullMsg;
		manager.outgoing[i].name = NullMsg;
		manager.outgoing[i].sender = NullMsg;
	}
	Manager_Scheduler();
}

static void startTech()
{
	Acquire(initLock);
	tech.index = 0;
	tech.replyTo = -1;
	tech.msgLock = CreateLock("tMsgLock", 8);
	tech.myName = 0;
	tech.sleepCon = CreateCondition("tSleepCon",9);
	tech.numTakersReplyNeeded = 0;
	tech.status = IDLE;
	tech.numMsgs = 0;
	Acquire(loopLock);
	for(i = 0; i < 3; i++)
	{
		tech.msgList[i].name = NullMsg;
		tech.msgList[i].sender = NullMsg;
	}
	Release(loopLock);
	MovieTech_Scheduler();
}

static void startLobby()
{
	Acquire(initLock);
	lobby.index = 0;
	lobby.replyTo = -1;
	lobby.msgLock = CreateLock("lMsgLock", 8);
	lobby.myName = 0;
	lobby.sleepCon = CreateCondition("lSleep", 6);
	lobby.state = IDLING;
	lobby.repliesWaiting = 0;
	lobby.msgsToSend = 0;
	lobby.numInLobby = 0;
	lobby.numMsgs = 0;
	
	Acquire(loopLock);
	lobby.custInLobby[MAX_CUSTOMERS/MAX_GROUP_SIZE + 1];
	lobby.custToAsk[MAX_CUSTOMERS/MAX_GROUP_SIZE + 1];
	for(i = 0; i < MAX_CUSTOMERS/MAX_GROUP_SIZE + 1; i++)
	{
		lobby.custInLobby[i] = -1;
		lobby.custToAsk[i] = -1;
		if(i < 3)
		{
			lobby.msgList[i].name = NullMsg;
			lobby.msgList[i].sender = NullMsg;
		}
	}
	Release(loopLock);
	Lobby_Scheduler();
}

void SendMessage(int type, int index, int name, int sender)
{	
	if(type == T_CUSTOMER)
	{
		Acquire(customers[index].msgLock);
		customers[index].msgList[customers[index].numMsgs].name = name;
		customers[index].msgList[customers[index].numMsgs].sender = sender;
		customers[index].numMsgs = customers[index].numMsgs + 1;
		Signal(customers[index].sleepCon, customers[index].msgLock);
		Release(customers[index].msgLock);
	}else if(type == T_TICKETCLERK)
	{
		Acquire(tixClerks[index].msgLock);
		tixClerks[index].msgList[tixClerks[index].numMsgs].name = name;
		tixClerks[index].msgList[tixClerks[index].numMsgs].sender = sender;
		tixClerks[index].numMsgs = tixClerks[index].numMsgs + 1;
		Signal(tixClerks[index].sleepCon, tixClerks[index].msgLock);
		Release(tixClerks[index].msgLock);
	}else if(type == T_FOODCLERK)
	{
		Acquire(foodClerks[index].msgLock);
		foodClerks[index].msgList[foodClerks[index].numMsgs].name = name;
		foodClerks[index].msgList[foodClerks[index].numMsgs].sender = sender;
		foodClerks[index].numMsgs = foodClerks[index].numMsgs + 1;
		Signal(foodClerks[index].sleepCon, foodClerks[index].msgLock);
		Release(foodClerks[index].msgLock);
	}else if(type == T_TICKETTAKER)
	{
		Acquire(tixTakers[index].msgLock);
		tixTakers[index].msgList[tixTakers[index].numMsgs].name = name;
		tixTakers[index].msgList[tixTakers[index].numMsgs].sender = sender;
		tixTakers[index].numMsgs = tixTakers[index].numMsgs + 1;
		Signal(tixTakers[index].sleepCon, tixTakers[index].msgLock);
		Release(tixTakers[index].msgLock);
	}else if(type == T_MANAGER)
	{
		Acquire(manager.msgLock);
		manager.msgList[manager.numMsgs].name = name;
		manager.msgList[manager.numMsgs].sender = sender;
		manager.numMsgs = manager.numMsgs + 1;
		Signal(manager.sleepCon, manager.msgLock);
		Release(manager.msgLock);
	}else if(type == T_LOBBY)
	{
		Acquire(lobby.msgLock);
		lobby.msgList[lobby.numMsgs].name = name;
		lobby.msgList[lobby.numMsgs].sender = sender;
		lobby.numMsgs = lobby.numMsgs + 1;
		Signal(lobby.sleepCon, lobby.msgLock);
		Release(lobby.msgLock);
	}else if(type == T_MOVIETECH)
	{
		Acquire(tech.msgLock);
		tech.msgList[tech.numMsgs].name = name;
		tech.msgList[tech.numMsgs].sender = sender;
		tech.numMsgs = tech.numMsgs + 1;
		Signal(tech.sleepCon, tech.msgLock);
		Release(tech.msgLock);
	}
}

int getRandPercent()
{
	return Random();
}

int main()
{
	startCineplex();
}